package com.xdb.sqlparser.builder.impl;

import java.util.List;
import java.util.Map;

import com.xdb.sqlparser.ast.SQLExpr;
import com.xdb.sqlparser.ast.SQLStatement;
import com.xdb.sqlparser.ast.expr.SQLBinaryOperator;
import com.xdb.sqlparser.ast.expr.SQLIdentifierExpr;
import com.xdb.sqlparser.ast.statement.SQLExprTableSource;
import com.xdb.sqlparser.ast.statement.SQLUpdateSetItem;
import com.xdb.sqlparser.ast.statement.SQLUpdateStatement;
import com.xdb.sqlparser.builder.SQLUpdateBuilder;
import com.xdb.sqlparser.dialect.mysql.ast.statement.MySqlUpdateStatement;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleUpdateStatement;
import com.xdb.sqlparser.util.SQLUtils;
import com.xdb.util.JDBCConstants;

public class SQLUpdateBuilderImpl extends SQLBuilderImpl implements SQLUpdateBuilder {

	private SQLUpdateStatement stmt;
	private String dbType;

	public SQLUpdateBuilderImpl(String dbType) {
		this.dbType = dbType;
	}

	public SQLUpdateBuilderImpl(String sql, String dbType) {
		List<SQLStatement> stmtList = SQLUtils.parseStatements(sql, dbType);

		if (stmtList.size() == 0) {
			throw new IllegalArgumentException("not support empty-statement :" + sql);
		}

		if (stmtList.size() > 1) {
			throw new IllegalArgumentException("not support multi-statement :" + sql);
		}

		SQLUpdateStatement stmt = (SQLUpdateStatement) stmtList.get(0);
		this.stmt = stmt;
		this.dbType = dbType;
	}

	public SQLUpdateBuilderImpl(SQLUpdateStatement stmt, String dbType) {
		this.stmt = stmt;
		this.dbType = dbType;
	}

	@Override
	public SQLUpdateBuilderImpl limit(int rowCount) {
		throw new UnsupportedOperationException();
	}

	@Override
	public SQLUpdateBuilderImpl limit(int rowCount, int offset) {
		throw new UnsupportedOperationException();
	}

	@Override
	public SQLUpdateBuilderImpl from(String table) {
		return from(table, null);
	}

	@Override
	public SQLUpdateBuilderImpl from(String table, String alias) {
		SQLUpdateStatement update = getSQLUpdateStatement();
		SQLExprTableSource from = new SQLExprTableSource(new SQLIdentifierExpr(table), alias);
		update.setTableSource(from);
		return this;
	}

	@Override
	public SQLUpdateBuilderImpl where(String expr) {
		SQLUpdateStatement update = getSQLUpdateStatement();

		SQLExpr exprObj = SQLUtils.toSQLExpr(expr, dbType);
		update.setWhere(exprObj);

		return this;
	}

	@Override
	public SQLUpdateBuilderImpl whereAnd(String expr) {
		SQLUpdateStatement update = getSQLUpdateStatement();

		SQLExpr exprObj = SQLUtils.toSQLExpr(expr, dbType);
		SQLExpr newCondition = SQLUtils.buildCondition(SQLBinaryOperator.BooleanAnd, exprObj, false, update.getWhere());
		update.setWhere(newCondition);

		return this;
	}

	@Override
	public SQLUpdateBuilderImpl whereOr(String expr) {
		SQLUpdateStatement update = getSQLUpdateStatement();

		SQLExpr exprObj = SQLUtils.toSQLExpr(expr, dbType);
		SQLExpr newCondition = SQLUtils.buildCondition(SQLBinaryOperator.BooleanOr, exprObj, false, update.getWhere());
		update.setWhere(newCondition);

		return this;
	}

	public SQLUpdateBuilderImpl set(String... items) {
		SQLUpdateStatement update = getSQLUpdateStatement();
		for (String item : items) {
			SQLUpdateSetItem updateSetItem = SQLUtils.toUpdateSetItem(item, dbType);
			update.addItem(updateSetItem);
		}

		return this;
	}

	public SQLUpdateBuilderImpl setValue(Map<String, Object> values) {
		for (Map.Entry<String, Object> entry : values.entrySet()) {
			setValue(entry.getKey(), entry.getValue());
		}

		return this;
	}

	public SQLUpdateBuilderImpl setValue(String column, Object value) {
		SQLUpdateStatement update = getSQLUpdateStatement();

		SQLExpr columnExpr = SQLUtils.toSQLExpr(column, dbType);
		SQLExpr valueExpr = toSQLExpr(value, dbType);

		SQLUpdateSetItem item = new SQLUpdateSetItem();
		item.setColumn(columnExpr);
		item.setValue(valueExpr);
		update.addItem(item);

		return this;
	}

	public SQLUpdateStatement getSQLUpdateStatement() {
		if (stmt == null) {
			stmt = createSQLUpdateStatement();
		}
		return stmt;
	}

	public SQLUpdateStatement createSQLUpdateStatement() {
		if (JDBCConstants.MYSQL.equals(dbType)) {
			return new MySqlUpdateStatement();
		}

		if (JDBCConstants.ORACLE.equals(dbType)) {
			return new OracleUpdateStatement();
		}

		return new SQLUpdateStatement();
	}

	public String toString() {
		return SQLUtils.toSQLString(stmt, dbType);
	}
}
